/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.wider.foundation.concurrency;

import com.wider.foundation.DLogger;
import com.wider.foundation.Named;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;

/**
 * This class defines a Named, Stoppable, Pausable, Joinable individual execution of code.
 * @author William Choi <avextk@gmail.com>
 * @see Named
 * @see Stoppable
 * @see Pausable
 * @see Joinable
 * @see Runnable
 * @see Executor
 * 
 */
public class Execution implements Named, Stoppable, Pausable, Joinable, Runnable, Executor {
    private static final int    DEFAULT_POLL_PERIOD = 3200; //millis
    protected final String      instnid;
    protected DLogger           logger;
    protected Runnable          task;
   
    private boolean         stopped;
    private boolean         paused;
    private Thread          thread;


    /**
     * Constructor to instanciate an Execution with a given name.
     * @param given     The given name of this instance.
     */
    public Execution(String given){
        stopped = true;
        paused  = true;
        task    = null;
        instnid = given;
        logger  = DLogger.getInstance(this);
        thread  = new Thread(this);

        logger.debug("Execution constructed.");
    }

    /**
     * Method for user to assign a task into this instance but DO NOT kick start the execution of this task.
     * @param assignment    The task to be assigned.
     * @throws RejectedExecutionException The assignment will be rejected while the previous assigned task has not been completed.
     */
     public void assign(Runnable assignment) throws RejectedExecutionException {
        synchronized(instnid){
            if (null != task)
                throw new RejectedExecutionException("Executor is temporarily unavailable");

            logger.trace("Assigning task");
            task = assignment;
            logger.trace("Task assigned.");
        }
    }

    /**
     * Implemented method of the Runnable interface. This forms a Main Loop of the Execution context.
     * In order to utilize full features of the Execution class, DO NOT override this implementation.
     */
    @Override
    public void run() {
        logger.trace("Entering...");
        while(true){
            synchronized(this){
                if (stopped){
                    logger.verbose("Execution runner is stopped.");
                    break;
                }
                else if(paused)
                {
                    logger.verbose("Execution runner is paused, waiting to start");
                    try {
                        this.wait(DEFAULT_POLL_PERIOD);
                    } catch (InterruptedException ex) {
                        logger.warn(ex);
                    }
                    continue;
                }
            }
            //Alive state: stopped == false && paused == false && task != null
            //TODO: Since the execute may be overrided, that means this may not only execute the Runnable.run(),
            //      but also some other code which may cause blocking the thread
            //      If the task implements Cancellable, managment board will first do task.cancel() before Execution.stop()
            //      So, Gotta find a way to stop the blocking code in the overrided "execute()"
            execute();
        }
        logger.trace("Exiting...");
    }

    /**
     * Assign and execute the desired task. This is the short form of <code>assign(Runnable); execute();</code>
     * @param assignment
     * @throws RuntimeException
     */
    @Override
    public void execute(Runnable assignment) throws RuntimeException {
        assign(assignment);
        start();
    }

    /**
     * Context wrapper for executing the assigned Runnable's <code>run()</code> method.
     * @throws IllegalStateException While no task has been assigned.
     */
    public void execute() throws IllegalStateException {
        synchronized (instnid){
            if (null == task)
                throw new IllegalStateException("No task to execute.");

            logger.trace("Start executing the assigned task.");
            task.run();
            task = null;
            logger.trace("Completed executing the assigned task.");
        }
        pause();
    }

    /**
     * Kick to start the context Main Loop of this Execution.
     * @return  True if started; False otherwise.
     */
    @Override
    public boolean start() {
        synchronized (this){
            if (paused || stopped){
                logger.debug("Kick to start the execution of the assigned task.");
                paused  = false;
                stopped = false;
                if (!thread.isAlive()){
                    logger.debug("Start a new thread for this execution...");
                    thread = null;
                    thread = new Thread(this);
                    thread.start();
                }
                this.notifyAll();
            }
        }
        return !stopped;
    }

    /**
     * Kick to stop the context Main Loop of this Execution
     * @return  True if started; False otherwise.
     */
    @Override
    public boolean stop() {
        boolean ret = true;
        if (null != task){
            if (task instanceof Pausable)
                ret = ((Pausable) task).pause();
            if (task instanceof Stoppable)
                ret = ((Stoppable) task).stop();
        }

        logger.debug("Setting the execution as stopped...");
        synchronized(this){
            stopped = ret;
        }
        logger.debug("execution is being stopped.");
        return stopped;
    }

    /**
     * Pause the current active Execution
     * @return  True if paused; false otherwise.
     */
    @Override
    public boolean pause() {
        boolean ret = true;
        if ((null != task) && (task instanceof Pausable)){
            ret = ((Pausable) task).pause();
        }

        logger.debug("Setting the execution as paused...");
        synchronized(this){
            paused = ret;
        }
        logger.debug("execution is paused.");
        return paused;
    }

    /**
     * Resume the paused Execution
     * @return  True if kicked to resume; false otherwise.
     */
    @Override
    public boolean resume() {
        return start();
    }

    /**
     * Check if this Execution is started
     * @return  True if so; false otherwise.
     */
    @Override
    public boolean isStarted() {
        return !(paused || stopped);
    }

    /**
     * Check if this Execution is stopped
     * @return  True if so; false otherwise.
     */
    @Override
    public boolean isStopped() {
        return stopped;
    }

    /**
     * Check if this Execution is paused
     * @return  True if so; false otherwise.
     */
    @Override
    public boolean isPaused() {
        return paused;
    }

    /**
     * Method for user to wait for the Execution thread to join back.
     * @throws InterruptedException
     */
    @Override
    public void join() throws InterruptedException{
        logger.trace("Waiting for the thread to join back...");
        thread.join();
        logger.trace("The thread has joined back.");
    }

    /**
     * Implemented method of the Named interface.
     * @return  The given name of the instance.
     */
    @Override
    public String getName() {
        return instnid;
    }
}
